
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <unistd.h>

#include <libifconfig.h>
#include <libplatereport.h>

struct handle_server
{
	Lib_Epoll *epoll;

	int server_fd;
	int client_fd;
};

struct handle_test
{
	char connect_addr[32];
	unsigned short connect_port;

	char server_addr[32];
	unsigned short server_port;

	unsigned int magic;
};

static void send_to_server(int fd, unsigned int type, unsigned int sync = 0, void *data = NULL, unsigned int size = 0)
{
	struct plate_report_packet packet;

	memset(&packet, 0, sizeof(packet));

	packet.head.magic = 0x84429521;
	packet.head.type = type;
	packet.head.sync = sync;

	if(NULL != data && 0 < size)
	{
		packet.head.size = MIN(sizeof(packet.data), size);

		memcpy(packet.data, data, packet.head.size);
	}

	send(fd, (void *)&packet, sizeof(packet.head) + packet.head.size, 0);
}

static void handle_beat(int fd)
{
	send_to_server(fd, 0x1000);
}

static void handle_ask(int fd, int sync)
{
	send_to_server(fd, 0x1001, sync);
}

static int connect_to(const char *addr, unsigned short port)
{
	int server_fd = -1;

	struct sockaddr_in server_addr;

	if(NULL == addr || 0 == port)
	{
		goto out;
	}

	server_fd = socket(AF_INET, SOCK_STREAM, 0);

	if(0 > server_fd)
	{
		goto out;
	}

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr.s_addr = inet_addr(addr);

	if(0 != connect(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)))
	{
		goto out1;
	}

	goto out;

out1:
	if(0 <= server_fd)
	{
		close(server_fd);
		server_fd = -1;
	}
out:
	return server_fd;
}

static void handle_socket(void *arg, int fd)
{
	struct handle_server *handle = (struct handle_server *)arg;

	struct plate_report_packet packet;

	memset(&packet, 0, sizeof(packet));

	if(handle->client_fd == fd)
	{
		int ret = recv(fd, packet.data, sizeof(packet.data), 0);

		if(0 < ret)
		{
			if(0 <= handle->server_fd)
			{
				send_to_server(handle->server_fd, 0x1, 0, packet.data, ret);
			}
		}
	}
	else
	{
		if(0 < recv(fd, &packet, sizeof(packet), 0))
		{
			handle_ask(fd, packet.head.sync);

			switch(packet.head.type)
			{
				case 0x0:
					if(0 <= handle->client_fd)
					{
						::send(handle->client_fd, packet.data, packet.head.size, 0);
					}
					break;
				case 0x10:
					{
						struct handle_test *test = (struct handle_test *)packet.data;

						printf("create link: %s:%d <=> %s:%d | 0x%08X\n", test->connect_addr, test->connect_port, test->server_addr, test->server_port, test->magic);

						handle->client_fd = connect_to(test->connect_addr, test->connect_port);

						if(0 <= handle->client_fd)
						{
							if(true == handle->epoll->add(handle->client_fd))
							{
								printf("ok, test->magic: 0x%08X\n", test->magic);
								send_to_server(handle->server_fd, 0x10, 0, test, sizeof(*test));
							}
							else
							{
								printf("epoll add client_fd failed\n");

								close(handle->client_fd);
								handle->client_fd = -1;
							}
						}
						else
						{
							printf("connect_to client_fd failed\n");
						}
					}
					break;
				default:
					break;
			}
		}
		else
		{
			handle->epoll->del(fd);

			close(fd);
			fd = -1;
		}
	}
}

int main(int argc, char **argv)
{
	int oc = 0;

	char hostaddr[32] = {0};
	unsigned short port = 0;

	struct handle_server my_handle;

	if(1 == argc)
	{
		printf("-h\t-> host\n-p\t-> port\n");
	}

	while(0 < (oc = getopt(argc, argv, "h:p:")))
	{
		switch(oc)
		{
			case 'h':
				strncpy(hostaddr, optarg, sizeof(hostaddr));
				if(0 > strlen(hostaddr))
				{
					goto out;
				}
				break;
			case 'p':
				port = (unsigned short)strtoul(optarg, NULL, 0);
				if(0 == port)
				{
					goto out;
				}
				break;
			case '?':
				goto out;
				break;
			case ':':
				goto out;
				break;
		}
	}

	if(0 > strlen(hostaddr) || 0 == port)
	{
		goto out;
	}

	printf("host: %s:%d\n", hostaddr, port);

	memset(&my_handle, 0, sizeof(my_handle));

	my_handle.epoll = Lib_Epoll::getObject(512);

	if(NULL == my_handle.epoll)
	{
		return 0;
	}

	my_handle.server_fd = connect_to(hostaddr, port);

	if(0 > my_handle.server_fd)
	{
		return 0;
	}

	if(true != my_handle.epoll->add(my_handle.server_fd))
	{
		return 0;
	}

	send_to_server(my_handle.server_fd, 0x0);

	while(1)
	{
		my_handle.epoll->wait(100, handle_socket, &my_handle);

		handle_beat(my_handle.server_fd);
	}

out:
	return 0;
}
